/*
 * Copyright (C) 2009 Georgios J. Georgopoulos, All rights reserved.
 * 
 * This is free software; you can redistribute it and/or modify it under the
 * terms of the GNU Lesser General Public License as published by the Free
 * Software Foundation; either version 3 of the License, or (at your option) any
 * later version.
 * 
 * This software is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
 * details.
 * 
 * You should have received a copy of the GNU Lesser General Public License
 * along with this software; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA, or see the FSF
 * site: http://www.fsf.org.
 */
package org.gwt.mosaic.xul.client.ui;

import org.gwt.beansbinding.core.client.Binding;
import org.gwt.beansbinding.core.client.PropertyStateEvent;
import org.gwt.beansbinding.core.client.PropertyStateListener;
import org.gwt.mosaic.actions.client.Action;
import org.gwt.mosaic.actions.client.ActionMap;
import org.gwt.mosaic.actions.client.ToolButtonBindings;
import org.gwt.mosaic.application.client.Application;
import org.gwt.mosaic.ui.client.ToolButton;
import org.gwt.mosaic.ui.client.ToolButton.ToolButtonStyle;

import com.google.gwt.user.client.ui.Widget;

/**
 * A button that can be pressed by the user. Event handlers can be used to trap
 * mouse, keyboard and other events. You can specify the {@code Label} or the
 * button using the label attribute or by placing content inside the button.
 * 
 * @author georgopoulos.georgios(at)gmail.com
 * 
 */
public class Button extends Element {
  private static final long serialVersionUID = -3618082330445988304L;

  private transient ToolButton toolButton = null;

  // -----------------------------------------------------------------------
  /**
   * This should be set to a character that is used as a shortcut key. This
   * should be one of the characters that appears in the label of the element.
   */
  public static final String ACCESSKEY = "accesskey";

  public char getAccesskey() {
    return getCharacter(ACCESSKEY);
  }

  public void setAccesskey(char accesskey) {
    putCharacter(ACCESSKEY, accesskey);
  }

  // -----------------------------------------------------------------------
  /**
   * Indicates whether the element is checked or not.
   */
  public static final String CHECKED = "checked";

  public boolean isChecked() {
    return getBoolean(CHECKED);
  }

  public void setChecked(boolean checked) {
    putBoolean(CHECKED, checked);
  }

  // -----------------------------------------------------------------------
  /**
   * Set to the id of a command element that is being observed by the element.
   */
  public static final String COMMAND = "command";

  public String getCommand() {
    return getString(COMMAND);
  }

  public void setCommand(String command) {
    putString(COMMAND, command);
  }

  // -----------------------------------------------------------------------
  /**
   * Indicates whether the element is disabled or not. If this element is set to
   * true the element is disabled. Disabled elements are usually drawn with
   * grayed-out text. If the element is disabled, it does not respond to user
   * actions, it cannot be focused, and the command event will not fire.
   */
  public static final String DISABLED = "disabled";

  public boolean isDisabled() {
    return getBoolean(DISABLED);
  }

  public void setDisabled(boolean disabled) {
    putBoolean(DISABLED, disabled);
  }

  // -----------------------------------------------------------------------
  /**
   * The label that will appear on the element. If this is left out, no text
   * appears.
   */
  public static final String LABEL = "label";

  public String getLabel() {
    return getString(LABEL);
  }

  public void setLabel(String label) {
    putString(LABEL, label);
  }

  // -----------------------------------------------------------------------
  /**
   * The tab order of the element. The tab order is the order in which the focus
   * is moved when the user presses the "tab" key. Elements with higher tabindex
   * are later in the tab sequence.
   */
  public static final String TABINDEX = "tabindex";

  public int getTabindex() {
    return getInteger(TABINDEX);
  }

  public void setTabindex(int tabindex) {
    putInteger(TABINDEX, tabindex);
  }

  // -----------------------------------------------------------------------
  /**
   * The type of button. If this attribute is not present, a normal button is
   * created. Leave the attribute out for normal button.
   * 
   * <ul>
   * <li>{@code checkbox}: This type of button can be in two states. The user
   * can click the button to switch between the states.</li>
   * </ul>
   */
  public static final String TYPE = "type";

  public String getType() {
    return getString(TYPE);
  }

  public void setType(String type) {
    putString(TYPE, type);
  }

  // -----------------------------------------------------------------------
  private transient ToolButtonBindings toolButtonBindings = null;

  // -----------------------------------------------------------------------
  @Override
  protected Widget createUI() {
    if (toolButton == null) {
      toolButton = new ToolButton();
    }

    syncUI(toolButton);

    return toolButton;
  }

  // -----------------------------------------------------------------------
  @SuppressWarnings("unchecked")
  @Override
  protected void syncUI(Widget ui) {
    super.syncUI(ui);

    if (attributeMap.containsKey(ACCESSKEY)) {
      toolButton.setAccessKey(getAccesskey());
    }

    if (attributeMap.containsKey(LABEL)) {
      toolButton.setHTML(getLabel());
    }

    toolButton.setEnabled(!isDisabled());

    if (attributeMap.containsKey(TABINDEX)) {
      toolButton.setTabIndex(getTabindex());
    }

    if (attributeMap.containsKey(TYPE)) {
      if ("checkbox".equals(getType())) {
        toolButton.setStyle(ToolButtonStyle.CHECKBOX);
        toolButton.setChecked(isChecked());
      }
    }

    // Handle "command" attribute

    if (toolButtonBindings != null) {
      toolButtonBindings.unbind();
      toolButtonBindings = null;
    }

    if (attributeMap.containsKey(COMMAND)) {
      final ActionMap actionMap = Application.getInstance().getContext().getActionMap();
      final Action action = actionMap.get(getCommand());
      if (action != null) {
        toolButtonBindings = new ToolButtonBindings(action, toolButton);

        Binding nameBinding = toolButtonBindings.getBinding(Action.NAME);
        if (nameBinding != null) {
          nameBinding.setSourceNullValue(getLabel());
          nameBinding.getTargetProperty().addPropertyStateListener(
              nameBinding.getTargetObject(), new PropertyStateListener() {
                public void propertyStateChanged(PropertyStateEvent pse) {
                  Button.this.setLabel((String) pse.getNewValue());
                }
              });
        }

        Binding shortDescrBinding = toolButtonBindings.getBinding(Action.SHORT_DESCRIPTION);
        if (shortDescrBinding != null) {
          shortDescrBinding.setSourceNullValue(getTooltiptext());
          shortDescrBinding.getTargetProperty().addPropertyStateListener(
              shortDescrBinding.getTargetObject(), new PropertyStateListener() {
                public void propertyStateChanged(PropertyStateEvent pse) {
                  Button.this.setTooltiptext((String) pse.getNewValue());
                }
              });
        }

        Binding smallIconBinding = toolButtonBindings.getBinding(Action.SMALL_ICON);
        if (smallIconBinding != null) {
          // smallIconBinding.setSourceNullValue(.action......);
          smallIconBinding.getTargetProperty().addPropertyStateListener(
              smallIconBinding.getTargetObject(), new PropertyStateListener() {
                public void propertyStateChanged(PropertyStateEvent pse) {
                  // Button.this.setLabel((String) pse.getNewValue());
                }
              });
        }

        Binding enabledBinding = toolButtonBindings.getBinding("enabled");
        if (enabledBinding != null) {
          // enabledBinding.setSourceNullValue(.action......);
          enabledBinding.getTargetProperty().addPropertyStateListener(
              enabledBinding.getTargetObject(), new PropertyStateListener() {
                public void propertyStateChanged(PropertyStateEvent pse) {
                  Button.this.setDisabled(!((Boolean) pse.getNewValue()));
                }
              });
        }

        Binding selectedBinding = toolButtonBindings.getBinding("selected");
        if (selectedBinding != null) {
          // selectedBinding.setSourceNullValue(.action......);
          selectedBinding.getTargetProperty().addPropertyStateListener(
              selectedBinding.getTargetObject(), new PropertyStateListener() {
                public void propertyStateChanged(PropertyStateEvent pse) {
                  Button.this.setChecked((Boolean) pse.getNewValue());
                }
              });
        }

        Binding visibleBinding = toolButtonBindings.getBinding("visible");
        if (visibleBinding != null) {
          // visibleBinding.setSourceNullValue(.action......);
          visibleBinding.getTargetProperty().addPropertyStateListener(
              visibleBinding.getTargetObject(), new PropertyStateListener() {
                public void propertyStateChanged(PropertyStateEvent pse) {
                  Button.this.setHidden(!((Boolean) pse.getNewValue()));
                }
              });
        }

        toolButtonBindings.bind();
      }
    }
  }

}
